using System;
using System.Collections.Generic;
using System.Text;
using MCromwell.StaffIntranet.Core;
using MCromwell.StaffIntranet.DependencyInjection;
using MCromwell.StaffIntranet.Task.Infrastructure;
using PostSharp.Extensibility;

namespace MCromwell.StaffIntranet.Task.Tasks
{
    [TaskExceptionHandler(AttributeTargetMemberAttributes=MulticastAttributes.Public, AttributeTargetElements=MulticastTargets.Method)]
    public class AuthenticationTask : AbstractTask, IAuthenticationTask
    {
        private IAdministrationRepository administrationRepository = null;
        private const int LOGIN_SESSION_TIMEOUT_MINUTES = 30;

        public AuthenticationTask(IAdministrationRepository administrationRepository)
        {
            if (administrationRepository == null)
                throw new ArgumentNullException("administratorRepository", "administratorRepository cannot be null");

            this.administrationRepository = administrationRepository;
        }

        public AuthenticationTask()
        {
            this.administrationRepository = IoC.Resolve<IAdministrationRepository>();
        }

        [WrapExceptionWith(typeof(ProblemStartingLoginSession))]
        public bool AuthenticateAgainst(Credentials credentials, out Guid authenticationToken)
        {
            authenticationToken = Guid.Empty;
            Administrator foundAdministrator = null;

            try
            {
                foundAdministrator = FindAdministrator(credentials);
                if (foundAdministrator == null)
                    return false;

                if (!foundAdministrator.AuthenticateAgainst(credentials))
                    return false;
            }
            catch (Exception ex)
            {
                Log(ex);
                throw;
            }
            
            try
            {
                LoginSession startedSession = administrationRepository.StartSessionFor(foundAdministrator);
                authenticationToken = startedSession.Id;
                return true;
            }
            catch (Exception ex)
            {
                Log(ex);
                throw new ProblemStartingLoginSession(ex);    
            }
        }
                
        private Administrator FindAdministrator(Credentials credentials)
        {
            Administrator foundAdministrator = administrationRepository.FindAdministratorUsing(credentials.Username);
            return foundAdministrator;
        }

        [WrapExceptionWith(typeof(ProblemRetrievingSessionException))]
        public bool CheckSessionAgainst(Guid sessionId)
        {
            LoginSession foundSession = administrationRepository.FindLoginSessionBy(sessionId);
            if (foundSession != null)
            {
                TimeSpan inactivityDuration = DateTime.Now.Subtract(foundSession.LastActivity);
                if (inactivityDuration.TotalMinutes > LOGIN_SESSION_TIMEOUT_MINUTES)
                    return false;
                
                UpdateLastActivityForSession(foundSession);
                return true;
            }
            else
                return false;
        }

        private void UpdateLastActivityForSession(LoginSession foundSession)
        {
            foundSession.LastActivity = DateTime.Now;
            administrationRepository.Update(foundSession);
        }
                
        [WrapExceptionWith(typeof(ProblemInvalidatingSessionException))]
        public SessionInvalidationResult InvalidateSessionFor(Guid id)
        {
            try
            {
                LoginSession session = administrationRepository.FindLoginSessionBy(id);
                if (session == null)
                    return SessionInvalidationResult.NotFound;

                administrationRepository.Delete(session);
                return SessionInvalidationResult.Invalidated;
            }
            catch (Exception ex)
            {
                Log(ex);
                throw;
            }
        }
    }
}
